//==============================================================================
// Copyright (c) 2008-2013 Niall Ryan. All Rights Reserved.
//==============================================================================

#ifndef HYDRA_ATOMIC_ATOMICMARKABLEPTR_H
#define HYDRA_ATOMIC_ATOMICMARKABLEPTR_H 1

namespace Hydra
{
	template<typename T>
	class AtomicMarkablePtr
	{
	public:
		AtomicMarkablePtr() { }
		AtomicMarkablePtr(T* ptr, bool mark)	{ set(ptr, mark); }
		void set(T* ptr, bool mark)
		{
			assert((reinterpret_cast<intptr_t>(ptr)&1) == 0);
			m_ptrAndMark = reinterpret_cast<T*>(reinterpret_cast<intptr_t>(ptr) | (mark ? 1 : 0));
		}
		T* getPtr()
		{
			T* ptrAndMark = m_ptrAndMark;
			return reinterpret_cast<T*>(reinterpret_cast<intptr_t>(ptrAndMark) & ~1);
		}
		T* get(bool& mark)
		{
			T* ptrAndMark = m_ptrAndMark;	//single atomic read
			mark = (reinterpret_cast<intptr_t>(ptrAndMark) & 1) ? true : false;
			return reinterpret_cast<T*>(reinterpret_cast<intptr_t>(ptrAndMark) & ~1);
		}
		void compareAndSwap(T* value, bool mark, T* compare, bool compareMark, T*& result, bool& resultMark)
		{
			assert((reinterpret_cast<intptr_t>(value)&1) == 0);
			assert((reinterpret_cast<intptr_t>(compare)&1) == 0);
			T* newPtrAndMark = reinterpret_cast<T*>(reinterpret_cast<intptr_t>(value) | (mark ? 1 : 0));
			T* comparePtrAndMark = reinterpret_cast<T*>(reinterpret_cast<intptr_t>(compare) | (compareMark ? 1 : 0));
			T* oldPtrAndMark = m_ptrAndMark.compareAndSwap(newPtrAndMark, comparePtrAndMark);
			result = reinterpret_cast<T*>(reinterpret_cast<intptr_t>(oldPtrAndMark) & ~1);
			resultMark = (reinterpret_cast<intptr_t>(oldPtrAndMark) & 1) ? true : false;
		}
		bool compareAndSwap(T* value, bool mark, T* compare, bool compareMark)
		{
			assert((reinterpret_cast<intptr_t>(value)&1) == 0);
			assert((reinterpret_cast<intptr_t>(compare)&1) == 0);
			T* newPtrAndMark = reinterpret_cast<T*>(reinterpret_cast<intptr_t>(value) | (mark ? 1 : 0));
			T* comparePtrAndMark = reinterpret_cast<T*>(reinterpret_cast<intptr_t>(compare) | (compareMark ? 1 : 0));
			T* oldPtrAndMark = m_ptrAndMark.compareAndSwap(newPtrAndMark, comparePtrAndMark);
			return (oldPtrAndMark == comparePtrAndMark);
		}
	private:
		Atomic<T*> m_ptrAndMark;	//mark stored in lowest bit
	};
}

#endif
